﻿using System;
using System.Diagnostics;
using System.Windows.Forms;
using FMOD;

namespace SteroClood.MediaPlayers
{
    public delegate void EndMusicEventHandler(object sender, EventArgs e);
    public static class FmodHelper
    {
        public class FmodStatus
        {
            public uint PositionMs;
            public uint LengthMs;
            public bool IsPlaying;
            public bool IsPaused;
        }

        private static FMOD.System system = null;
        private static FMOD.Sound sound = null;
        private static FMOD.Channel channel = null;
        private static Timer _timer = new Timer();

        private static CHANNEL_CALLBACK _channelEndCallback;
        public static event EndMusicEventHandler EndMusic;

        public static string LastError { get; set; }

        public static bool Initialize()
        {
            uint version = 0;
            FMOD.RESULT result;

            _timer.Interval = 50;
            _timer.Tick += new EventHandler(TimerTick);
            _timer.Start();

            /*
                Global Settings
            */
            result = FMOD.Factory.System_Create(ref system);
            if (FmodFailed(result))
            {
                return false;
            }

            result = system.getVersion(ref version);
            if (FmodFailed(result))
            {
                return false;
            }
            if (version < FMOD.VERSION.number)
            {
                MessageBox.Show("Error!  You are using an old version of FMOD " + version.ToString("X") + ".  This program requires " + FMOD.VERSION.number.ToString("X") + ".");
                Application.Exit();
            }

            result = system.init(1, FMOD.INITFLAGS.NORMAL, (IntPtr)null);
            if (FmodFailed(result))
            {
                return false;
            }

            _channelEndCallback = new CHANNEL_CALLBACK(OnEndMusic);
            return true;
        }

        private static RESULT OnEndMusic(IntPtr channelraw, FMOD.CHANNEL_CALLBACKTYPE type, IntPtr commanddata1, IntPtr commanddata2)
        {
            if (EndMusic != null)
                EndMusic(channel, new EventArgs());
            channel = null;
            return RESULT.OK;
        }

        public static void SetVolume(int value)
        {
            if (channel != null)
            {
                channel.setVolume((float)(value/100.0));
            }
        }

        static void TimerTick(object sender, EventArgs e)
        {
            if (system != null)
            {
                system.update();
            }
        }
        public static bool Shutdown()
        {
            try
            {
                _timer.Stop();
                FMOD.RESULT result;

                /*
                    Shut down
                */
                if (sound != null)
                {
                    result = sound.release();
                    if (FmodFailed(result))
                    {
                        return false;
                    }
                }
                if (system != null)
                {
                    result = system.close();
                    if (FmodFailed(result))
                    {
                        return false;
                    }
                    result = system.release();
                    if (FmodFailed(result))
                    {
                        return false;
                    }
                }
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine("Catch occur: " + ex.Message);
            }
            return true;
        }
        public static bool Load(string url)
        {
            FMOD.RESULT result = FMOD.RESULT.OK;
            //Stop();

            if (channel != null)
            {
                channel.setCallback(null);
            }

            if (sound != null)
            {
                result = sound.release();
                sound = null;
                if (FmodFailed(result))
                {
                    return false;
                }
            }

            channel = null;

            result = system.createSound(url, (FMOD.MODE._2D | FMOD.MODE.HARDWARE | FMOD.MODE.CREATESTREAM), ref sound);
            if (FmodFailed(result))
            {
                return false;
            }

            result = system.playSound(FMOD.CHANNELINDEX.FREE, sound, false, ref channel);
            if (FmodFailed(result))
            {
                return false;
            }
            channel.setCallback(_channelEndCallback);

            return true;
        }

        public static bool Pause()
        {
            FMOD.RESULT result = FMOD.RESULT.OK;
            bool paused = false;

            if (channel != null)
            {
                result = channel.getPaused(ref paused);
                if (FmodFailed(result))
                {
                    throw new Exception(LastError);
                }
                result = channel.setPaused(!paused);
                if (FmodFailed(result))
                {
                    throw new Exception(LastError);
                }
            }
            return !paused;
        }

        private static void Stop()
        {
            FMOD.RESULT result = FMOD.RESULT.OK;
            if (channel != null)
            {
                result = channel.stop();
                if (FmodFailed(result))
                {
                    throw new Exception(LastError);
                }
            }
        }

        public static FmodStatus GetStatus()
        {
            FMOD.RESULT result;

            /*
             * 
             * 
             *  fmodchn->getPaused( &playing );
  if ( !playing )
  {
   fmodchn->getSpectrum( specL, SPECLEN, 0, FMOD_DSP_FFT_WINDOW_BLACKMAN ); 
   fmodchn->getSpectrum( specR, SPECLEN, 1, FMOD_DSP_FFT_WINDOW_BLACKMAN ); 
  } else
	{
	 memset( specL, 0, SPECLEN*sizeof(float) );
	 memset( specR, 0, SPECLEN*sizeof(float) );	
	};
             * 
             * Might want to add a spectrum view for fun...
             */

            FmodStatus currentFmodStatus = new FmodStatus() { IsPaused = false, IsPlaying = false, LengthMs = 0, PositionMs = 0 };

            if (channel != null)
            {
                result = channel.isPlaying(ref currentFmodStatus.IsPlaying);
                if ((result != FMOD.RESULT.OK) && (result != FMOD.RESULT.ERR_INVALID_HANDLE))
                {
                    if (FmodFailed(result))
                    {
                        throw new Exception(LastError);
                    }
                }

                result = channel.getPaused(ref currentFmodStatus.IsPaused);
                if ((result != FMOD.RESULT.OK) && (result != FMOD.RESULT.ERR_INVALID_HANDLE))
                {
                    if (FmodFailed(result))
                    {
                        throw new Exception(LastError);
                    }
                }

                result = channel.getPosition(ref currentFmodStatus.PositionMs, FMOD.TIMEUNIT.MS);
                if ((result != FMOD.RESULT.OK) && (result != FMOD.RESULT.ERR_INVALID_HANDLE))
                {
                    if (FmodFailed(result))
                    {
                        throw new Exception(LastError);
                    }
                }

                result = sound.getLength(ref currentFmodStatus.LengthMs, FMOD.TIMEUNIT.MS);
                if ((result != FMOD.RESULT.OK) && (result != FMOD.RESULT.ERR_INVALID_HANDLE))
                {
                    if (FmodFailed(result))
                    {
                        throw new Exception(LastError);
                    }
                }
            }

            if (system != null)
            {
                system.update();
            }

            return currentFmodStatus;
        }

        private static bool FmodFailed(FMOD.RESULT result)
        {
            if (result != FMOD.RESULT.OK)
            {
                LastError = "FMOD error! " + result + " - " + FMOD.Error.String(result);
                return true;
            }
            return false;
        }
    }
}
